﻿namespace HelperLibrary;

public static class ExpressionExtensions
{
    public static PropertyInfo? GetPropertyInfo<TSource>(this Expression<Func<TSource, object>> expression)
    {
        var member = expression.Body as MemberExpression;
        if (member != null)
        {
            return member.Member as PropertyInfo;
        }
        return null;
    }

    private static Expression<T> Combine<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
    {
        var visitor = new ReplaceExpressionVisitor(first.Parameters[0]);
        var body1 = visitor.Visit(first.Body);
        var body2 = visitor.Visit(second.Body);
        return Expression.Lambda<T>(merge(body1, body2), first.Parameters[0]);
    }

    public static Expression<Func<TIn, bool>> Append<TIn>(this Expression<Func<TIn, bool>> expression, Expression<Func<TIn, bool>> append, Func<Expression, Expression, Expression> mergeType, bool cond = true)
    {
        if (cond)
        {
            return expression.Combine(append, mergeType);
        }
        return expression;
    }

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second, bool cond = true) => first.Append(second, Expression.AndAlso, cond);

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second, bool cond = true) => first.Append(second, Expression.Or, cond);

    private static Expression<Func<T, bool>>? Merge<T>(this IEnumerable<Expression<Func<T, bool>>> expressions, Func<Expression, Expression, Expression> merge)
    {
        if (expressions?.Any() ?? false)
        {
            var first = expressions.First();
            var others = expressions.Skip(1);
            foreach (var item in others)
            {
                first = first.Combine(item, merge);
            }
            return first;
        }
        return null;
    }

    public static Expression<Func<T, bool>>? MergeAnd<T>(this IEnumerable<Expression<Func<T, bool>>> expressions)
    {
        return expressions.Merge(Expression.AndAlso);
    }

    public static Expression<Func<T, bool>>? MergeOr<T>(this IEnumerable<Expression<Func<T, bool>>> expressions)
    {
        return expressions.Merge(Expression.Or);
    }
}

public class ReplaceExpressionVisitor(ParameterExpression Parameter) : ExpressionVisitor
{
    public ParameterExpression _Parameter { get; set; } = Parameter;

    protected override Expression VisitParameter(ParameterExpression p)
    {
        return _Parameter;
    }

    public override Expression? Visit(Expression? node)
    {
        return base.Visit(node);
    }
}
